﻿/******
 * TODO:
 *  1- ADD the enum HashType containing at least (NT, NTLM, MD5, MD4, SHA1, SHA2, TWOFISH, ...)
 *  2- make the command line arguments switchable (-h [HASH] ...)
 *  3- 
 * 
 */

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using System.Collections.Generic;

namespace DBruteServer
{

    /*****
     * Class PasswordRange: container to easilly carry infomration about password range.
     * 
     */
    public class PasswordRange
    {
        public string MinPass { get; set; }
        public string MaxPass { get; set; }
        public bool Tested { get; set; }

        public PasswordRange(string p_minPass="", string p_maxPass="")
        {
            this.MinPass = p_minPass;
            this.MaxPass = p_maxPass;
            this.Tested = false;
        }


    };

    /*****
     * Enum Charset: enumaration of the diffrent charset used in the password generation.
     * 
     */
    public enum Charset
    {
        NONE = "",
        NUM = "0123456789",
        ALPHA = "abcdefghijklmnopqrstuvwxyz",
        ALPHANUM = "abcdefghijklmnopqrstuvwxyz0123456789",
        ALPHANUMSPECIAL = "",
        ALPHACASE = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
        ALPHANUMCASE = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
        ALPHANUMSPECIALSCASE = ""
    }

    /*****
     * Class Program
     */
    class Program
    {
        private static Mutex mutPassFound = new Mutex();
        private static Mutex mutPassesRange= new Mutex();

        private const int numIterations = 1;
        private static bool gPassFound = false;
        private static string gPass = "";
        private static string gIp = "";
        private static int gPort = 0;
        private static string gHash = "";
        private static Charset gCharset = Charset.NONE;
        private static int gMinLength = 0;
        private static int gMaxLength = 0;
        private static string gAlgorithm = "";

        /*****
         * Methode Main: entry point of the program
         *  command line args:
         *  0 = prg name ?
         *  1 = ip:port
         *  2 = hash
         *  3 = Charset (num, in order they are placed in the enum, starting at 0)
         *  4 = minPassLenght
         *  5 = maxPassLenght
         *  6 = Algorithm (NT, SHA1, SHA2, MD4, MD5, ..)
         */
        public void Main(string[] args)
        {
            SetupVariables(args);
            StartListening();    
        }

        /*****
         * Method SetupVariables: used to setup the main variables of the server
         *  using the command line arguments
         * 
         */
        public void SetupVariables(string[] p_args)
        {
            //If we got the right number of arguments, we parse them into variables
            // otherwise, console.write a message showing the expected command line arguments.
            if (p_args.Length == 8)
            {
                gIp = p_args[1].Substring(0, p_args[1].IndexOf(':')-1);
                gPort = 8000;
                gHash = p_args[2];
                gCharset = Charset.ALPHANUM;
                gMinLength = 4;
                gMaxLength = 8;
                gAlgorithm = "NT";
            }
            else
            {
                //FOR DEBUG
                gIp = "192.168.0.106";
                gPort = 8000;
                gHash = "121DAD055368E871C2C5054B66F6A9F3"; //dave
                gCharset = Charset.ALPHANUM;
                gMinLength = 4;
                gMaxLength = 8;
                gAlgorithm = "NT";
            }

        }

        public static string GetPassFound()
        {
            string pass;
            // Wait until it is safe to enter.
            mutPassFound.WaitOne();

            pass = gPass;

            // Release the Mutex.
            mutPassFound.ReleaseMutex();
            return pass;
        }

        public static void SetPassFound(bool p_passFound, string pass)
        {
            // Wait until it is safe to enter.
            mutPassFound.WaitOne();

            gPassFound = p_passFound;

            // Release the Mutex.
            mutPassFound.ReleaseMutex();
        }

        public static string GetUntestedRange()
        {
            return "";
        }

        public static void SetTestedRange(bool p_passFound, string pass)
        {

        }

        public void StartListening()
        {
            Console.WriteLine("Starting server..");
            try
            {
                List<Thread> clientList= new List<Thread>();
                
                IPAddress ipAdress = IPAddress.Parse(gIp);

                List<PasswordRange> passesRange = new List<PasswordRange>();
                passesRange = GeneratePasswordRange();
                
                // Initializes the Listener
                TcpListener listener = new TcpListener(ipAdress, gPort);

                // Start Listeneting at the specified port
                listener.Start();
                
                while(true)
                {
                    string pass= GetPassFound();
                    if (pass != "")
                    {
                        Console.WriteLine("Password found: " + pass);
                        break;
                    }

                    Console.WriteLine("Waiting for connection..");
                    Socket s = listener.AcceptSocket();
                    
                    //When accepted
                    Console.WriteLine("Connection accepted from"+ s.RemoteEndPoint);
                    PasswordRange passRange= new PasswordRange();
                    //TODO initiate passrange

                    //Create and start the thread
                    Thread oThread = new Thread(() => ClientThread(s, passRange, gCharset, gHash, gAlgorithm));
                    oThread.Start();

                    //Add the thread to the client list
                    clientList.Add(oThread);                                 
                }

            }
            catch(Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        //Genere une liste de mots de passe a tester sous la forme
        //0000-ZZZZZZZ
        //a0000000-aZZZZZZZ
        //b..
        private static List<PasswordRange> GeneratePasswordRange()
        {
            List<PasswordRange> passes= new List<PasswordRange>();
            string charset = gCharset.ToString();
            int minSize = gMinLength;
            int maxSize = gMaxLength;
            //int range = 1000000; //Logique a construire

            String minPass = "";
            String maxPass = "";
            char c;
            //Construit debut
            for(int i = 0; i <= minSize-1; i++)
            {
                c = charset[0];
                minPass += c;
            }

            //Construit fin, -2 pour avoir le dernier pass avant d'avoir le max
            for(int i = 0; i <= maxSize-2; i++)
            {
                c= charset[charset.Length-1];
                maxPass += c;
            }

            PasswordRange p = new PasswordRange(minPass, maxPass);
            passes.Add(p);
            
            //FOR DEBUG
            Console.WriteLine(minPass + '-' + maxPass);

            for(int i=0; i!= charset.Length; ++i)
            {
                PasswordRange passRange= new PasswordRange();
                passRange.MinPass = charset[i] + RepeatChar(charset[0], maxSize-1);             
                passRange.MaxPass = charset[i] + RepeatChar(charset[charset.Length - 1], maxSize - 1);
                
                //FOR DEBUG
                Console.WriteLine(passRange.MinPass + '-' + passRange.MaxPass);

                passes.Add(p);
            }
            return passes;
        }

        public static string RepeatChar(char charToRepeat, int times)
        {
            string result="";
            for (int i = 0; i != times; ++i)
            {
                result += charToRepeat;
            }

            return result;
        }

        //This method will be called when the thread is started
        private static void ClientThread(Socket s, PasswordRange passRange, Charset charset,
                                 string hash, string hashingType)
        {
            bool alive = true;
            String message = passRange.MinPass + '\\' + passRange.MaxPass +
                             '\\' + hash + '\\' + charset.ToString() + '\\' + hashingType;

            while (alive)
            {
                //Convert the message into byte[] and send it
                UTF8Encoding encoding = new UTF8Encoding();
                s.Send(encoding.GetBytes(message));

                //Wait for max 1 hour
                int receiveTimeout = 1000 * 60 * 60;
                s.ReceiveTimeout = receiveTimeout;

                byte[] responce = new byte[100];
                if (s.Connected)
                {
                    int err = s.Receive(responce);
                    string strResponce = encoding.GetString(responce);

                    string endingCode = strResponce.Substring(0, 1);
                    if (endingCode == "1") //reussi
                    {
                        string pass = strResponce.Substring(1, strResponce.Length - 2);
                        SetPassFound(true, pass);
                    }
                    else
                    {
                        //Mettre le tested a true pour dernier range testé
                        //Aller chercher autre range
                    }
                }
                else
                {
                    Console.WriteLine("Socket Disconnected.");
                    alive = false;
                }
            }         

            s.Disconnect(true);
        }
    }
}
